/****************************************************************
 *  This file created by writerB.php on 04/19/2007 at 13:16:40  *
 ****************************************************************
 *  writerB.php created by Tonk (tonk@ctonk.com) in 2007        *
 ****************************************************************
 *                 General Document Information
 *
 *  File Name: loginHandler.cpp
 *  Class Names: LoginHandler, LoginList
 *  Data members: 5
 *  Notes:
 ****************************************************************/

#include <arpa/telnet.h>
#include <iostream>
#include <vector>
#include "combined.h"
#include "log.h"
#include "loginHandler.h"
#include "stringutil.h"
#include "world.h"



int LoginHandler::numLoginHandlers = 0;
int LoginHandler::loginHandlerIndex = 0;


// Default constructor...
LoginHandler::LoginHandler() {
	setLoginHandlerID( ++loginHandlerIndex );
	setCharacter( NULL );
	setState( 0 );
	setFlags( 0 );
	// Increment the object count...
	numLoginHandlers++;
}

// Additional constructors...
LoginHandler::LoginHandler( Character* character ) {
	setName( character->getName() );
	setCharacter( character );
	setState( CHECK_NAME );
	setFlags( 0 );
}

// Copy constructor (Doesn't change _LoginHandlerID)...
LoginHandler::LoginHandler( const LoginHandler& ref ) {
	setLoginHandlerID( ++loginHandlerIndex );
	setName( ref.getName() );
	setOutput( ref.getOutput() );
	setCharacter( ref.getCharacter() );
	setState( ref.getState() );
	setFlags( ref.getFlags() );
	// Increment the object count...
	numLoginHandlers++;
}

// Destructor...
LoginHandler::~LoginHandler() {
	_character = NULL;
	// Decrement the object count...
	numLoginHandlers--;
}

// Assignment operator (Doesn't change _LoginHandlerID)...
LoginHandler& LoginHandler::operator = ( const LoginHandler& ref ) {
	setName( ref.getName() );
	setOutput( ref.getOutput() );
	setCharacter( ref.getCharacter() );
	setState( ref.getState() );
	setFlags( ref.getFlags() );
	return *this;
}

// Equality operator (Doesn't compare _LoginHandlerID)...
bool LoginHandler::operator == ( const LoginHandler& ref ) const {
	return	(
		   getName() == ref.getName()
		&& getOutput() == ref.getOutput()
		&& getCharacter() == ref.getCharacter()
		&& getState() == ref.getState()
		&& getFlags() == ref.getFlags()
		);
}

// Less than operator (Compares _LoginHandlerID)...
bool LoginHandler::operator < ( const LoginHandler& ref ) const {
	return ( getLoginHandlerID() < ref.getLoginHandlerID() );
}

// Greater than operator (Compares _LoginHandlerID)...
bool LoginHandler::operator > ( const LoginHandler& ref ) const {
	return ( getLoginHandlerID() > ref.getLoginHandlerID() );
}

// Protected Methods...
void LoginHandler::setLoginHandlerID( const int& LoginHandlerID ) { // protected
	_LoginHandlerID = LoginHandlerID;
}

bool LoginHandler::checkName( Character* character, const std::string& input ) { // protected
	std::string name = lowerString( loginTrim( input ) );
	name[0] = UPPER( name[0] );
	character->setName( name );

	// Check for existence of pfile...
	if ( !LoginList::Instance().found( name ) ) {
		character->setDisconnected( true );
		return true;
	}

	// Check for a double login attempt...
	if ( World::Instance().getCharacters().find( name ) != World::Instance().getCharacters().end() ) {
		character->setDisconnected( true );
		return true;
	}

	if ( !character->load() ) { // Did the pfile load correctly?
		character->setDisconnected( true );
		std::string message;
		message << name << "'s pfile failed to load.";
		log( message, SERVER );
		return true;
	} else {
		// Character exists, and the load was successful so we'll move on to ask for a password.
		char echo_off[] = {IAC, WILL, TELOPT_ECHO, '\0'};
		character->setName( name );
		character->Send( echo_off );
		character->Send( "password: " );
		setState( getState()+1 );
	}

	return false;
}

bool LoginHandler::checkPassword( Character* character, const std::string& input ) { // protected
	std::string buf;
	std::string password = trim( input );

	// Check password...
	if ( character->getPassword() != password ) {
		// Password was incorrect. Disconnect user.
		character->setDisconnected( true );
		return true;
	} else {
		// Password was correct. Update status and send a welcome message.
		//static const char echo_off[] = {IAC, WILL, TELOPT_ECHO, '\0'};
		World::Instance().omninet( character->getName() + " has connected from " + character->getSocket()->GetSocketIP(), character );
		character->Send( "Welcome!\n\r\n\r" );
		character->setStatus( CONNECTED );
		setState( getState()+1 );
		return true;
	}

	return true;
}

// Public accessor methods...
int LoginHandler::getLoginHandlerID( void ) const { // public
	return _LoginHandlerID;
}

void LoginHandler::setName( const std::string& name ) { // public
	_name = name;
	return;
}

std::string LoginHandler::getName( void ) const { // public
	return _name;
}

void LoginHandler::setOutput( const std::string& output ) { // public
	_output = output;
	return;
}

std::string LoginHandler::getOutput( void ) const { // public
	return _output;
}

void LoginHandler::setCharacter( Character* character ) { // public
	_character = character;
	return;
}

Character* LoginHandler::getCharacter( void ) const { // public
	return _character;
}

void LoginHandler::setState( const unsigned& state ) { // public
	_state = state;
	return;
}

unsigned LoginHandler::getState( void ) const { // public
	return _state;
}

void LoginHandler::setFlags( const unsigned& flags ) { // public
	_flags = flags;
	return;
}

unsigned LoginHandler::getFlags( void ) const { // public
	return _flags;
}

// General methods...
bool LoginHandler::fire( Character* character, const std::string& args ) { // public
	// LoginHandler::fire() evaluates input for a login and responds
	// accordingly. Returns true when the handler object needs to be
	// destroyed and removed from the list.
	std::string input = trim( args );

	switch( getState() ) {
		case CHECK_NAME:
			// args should contain the name of the player.
			return checkName( character, input );
		case CHECK_PASSWORD:
			// args should contain a password attempt.
			return checkPassword( character, input );
		default:
			// Something funky happened with the LoginHandler::_state variable.
			character->Send( "error\n\r\n\r" );
			character->setDisconnected( true );
			log( "Default hit in LoginHandler::fire() (LoginHandler::_state is corrupted)", SERVER );
			return true;
	}
}

// Static methods...

// Associated operators...
std::ostream& operator << ( std::ostream& out, const LoginHandler& loginHandler ) { // associated
	out << &loginHandler; // Just use the pointer version of the overloaded operator <<
	return out;
}

std::ostream& operator << ( std::ostream& out, const LoginHandler* loginHandler ) { // associated
	out << "LoginHandlerID:\t" << loginHandler->getLoginHandlerID() << "\n";
	out << "name:\t\t" << loginHandler->getName() << "\n";
	out << "output:\t" << loginHandler->getOutput() << "\n";
	out << "character:\t" << loginHandler->getCharacter()->getName() << "\n";
	out << "state:\t" << loginHandler->getState() << "\n";
	out << "flags:\t" << loginHandler->getFlags() << "\n";
	return out;
}



//////////////////////////////////////////////  LOGIN LIST  ////////////////////////////////////////////////
// Default constructor...
LoginList::LoginList( void ) {
	load();
}

// Destructor...
LoginList::~LoginList( void ) {
	save();
	for ( std::vector< Account* >::iterator it = getRegister().begin(); it != getRegister().end(); ) {
		delete (*it);
		it = getRegister().erase( it );
	}
}

// Public accessor methods...
std::vector< LoginHandler* >& LoginList::getHandlers( void ) { // public
	return _handlers;
}

std::vector< Account* >& LoginList::getRegister( void ) { // public
	return _register;
}

// Protected methods...
void LoginList::load( void ) { // protected
	std::string name, password;

	std::ifstream load( ACCOUNT_FILE );
	if ( load.fail() ) {
		std::string message;
		message << "Couldn't open " << ACCOUNT_FILE << " for reading.";
		log( message, SERVER );
		return;
	}

	while ( load.peek() != EOF ) {
		getline( load, name, '~' );
		getline( load, password );
		add( name, password );
	}

	load.close();
	load.clear();

	return;
}

void LoginList::save( void ) { // protected

	std::ofstream write( ACCOUNT_FILE );
	if ( write.fail() ) {
		std::string message;
		message << "Couldn't open " << ACCOUNT_FILE << " for reading.";
		log( message, SERVER );
		return;
	}

	for ( std::vector< Account* >::iterator it = getRegister().begin(); it != getRegister().end(); ++it )
		write << *it;

	write.close();
	write.clear();

	return;
}

// General methods...
void LoginList::add( const std::string& name, const std::string& password ) { // public
	getRegister().push_back( new Account( name, password ) );
	return;
}

void LoginList::drop( const std::string& name ) { // public
	for ( std::vector< Account* >::iterator it = getRegister().begin(); it != getRegister().end(); ++it )
		if ( (*it)->getName() == name ) {
			delete (*it);
			(*it) = NULL;
			getRegister().erase( it );
			return;
		}
	return;
}

bool LoginList::found( const std::string& name ) { // public
	for ( std::vector< Account* >::iterator it = getRegister().begin(); it != getRegister().end(); ++it )
		if ( (*it)->getName() == name )
			return true;
	return false;
}

void LoginList::act( Character* character, const std::string& input ) { // public
	// If character is new to the loginList, a new LoginHandler is created.
	// Otherwise, the correct loginHandler is called.  Then LoginHandler::fire() is executed.
	LoginHandler* lh = NULL;
	bool newLogin = true;

	for ( std::vector< LoginHandler* >::iterator it = getHandlers().begin(); it != getHandlers().end(); it++ ) {
		// Find out if this Character needs a new LoginHandler.
		if ( (*it)->getCharacter() == character ) {
			// This Character has a loginHandler already.
			newLogin = false;
			lh = (*it);
			break;
		}
	}

	if ( newLogin ) {
		// Create a new loginHandler in the list.
		lh = new LoginHandler( character );
		getHandlers().push_back( lh );
	}

	// loginHandler::fire() does the main work.  Also, it returns true
	// if it's done it's job, and needs to be ousted from the list.
	if ( lh ) {
		if ( lh->fire( character, input ) ) {
			// remove lh from the list
			for ( std::vector< LoginHandler* >::iterator it = getHandlers().begin(); it != getHandlers().end(); ) {
				if ( (*it) == lh ) {
					delete (*it);
					it = getHandlers().erase( it );
					break;
				} else {
					++it;
				}
			}
		}
	}

	return;
}

